import type { ChangeEvent } from 'react';
import { type FormEvent, useContext, useEffect, useRef, useState } from 'react';
import useToast from 'hooks/useToast';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
  clientUpdateUserPassword,
  queryPasswordPublicKey,
} from '@/services/api';
import type JSEncrypt from 'jsencrypt';
import { UserIdPageContext } from '@/contexts/user-id';
import type { IDifference } from '@/interfaces';
import diff from 'microdiff';
import Spinner from '@/app/[locale]/component/spinner/spinner';
import { getDiffData } from '@/lib/tool';

export default function UpdatePasswordUserIdPage() {
  const updatePasswordInputRef = useRef<HTMLInputElement>(null);
  const jsEncryptRef = useRef<JSEncrypt | null>(null);
  const { show } = useToast();
  const {
    source: { path },
    translatedFields,
  } = useContext(UserIdPageContext)!;
  const [isDisabledSave, setIsDisabledSave] = useState(true);
  const [differenceData, setDifferenceData] = useState<IDifference[]>([]);
  const [form, setForm] = useState<{
    password: string;
  }>({
    password: '',
  });

  useEffect(() => {
    const diffData = diff(
      {
        password: '',
      },
      form,
    );
    setDifferenceData(diffData);
    setIsDisabledSave(diffData.length === 0);
  }, [form]);

  const clientUpdateUserPasswordMutation = useMutation(
    clientUpdateUserPassword,
  );

  const queryPasswordPublicKeyQuery = useQuery(
    ['/key', '/public', '/password'],
    async () => {
      return (await queryPasswordPublicKey()) as string;
    },
    {
      enabled: false,
    },
  );

  async function onSubmitUpdatePassword(e: FormEvent<HTMLFormElement>) {
    try {
      e.stopPropagation();
      e.preventDefault();

      const data = getDiffData(differenceData);
      const password = data.password.trim();
      if (!password) {
        show({
          type: 'DANGER',
          message: translatedFields.passwordCannotBeEmpty,
        });
        return;
      }

      await clientUpdateUserPasswordMutation.mutateAsync({
        data: {
          password: await getEncryptedPassword(password),
        },
      });

      setForm({ ...form, password: '' });

      show({
        type: 'SUCCESS',
        message: translatedFields.updatePasswordCompleted,
      });
    } catch (e) {
      clientUpdateUserPasswordMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  async function getJsEncrypt(): Promise<JSEncrypt> {
    let jsEncrypt;
    if (jsEncryptRef.current) {
      jsEncrypt = jsEncryptRef.current;
    } else {
      const JSEncrypt = (await import('jsencrypt')).JSEncrypt;
      jsEncrypt = new JSEncrypt();
      jsEncryptRef.current = jsEncrypt;
    }
    return jsEncrypt;
  }

  async function getEncryptedPassword(password: string): Promise<string> {
    const jsEncrypt = await getJsEncrypt();
    const result = await queryPasswordPublicKeyQuery.refetch({
      throwOnError: true,
    });
    const publicKey = result.data;
    if (!publicKey) {
      throw translatedFields.getPasswordDataFail;
    }

    jsEncrypt.setPublicKey(publicKey);
    const encryptedData = jsEncrypt.encrypt(password);
    if (!encryptedData) {
      throw translatedFields.getPasswordDataFail;
    }

    return encryptedData;
  }

  function onChangeForm(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name;
    const value = e.target.value;
    setForm({ ...form, [name]: value });
  }

  return (
    <form className="vstack gap-4" onSubmit={onSubmitUpdatePassword}>
      <input
        readOnly
        hidden
        name="username"
        type="text"
        className="form-control"
        autoComplete="username"
      />
      <input
        name="password"
        ref={updatePasswordInputRef}
        type="password"
        className="form-control"
        autoComplete="new-password"
        value={form.password}
        onChange={onChangeForm}
      />

      <button
        type="submit"
        disabled={clientUpdateUserPasswordMutation.isLoading || isDisabledSave}
        className="btn btn-outline-success col col-lg-2 my-4"
      >
        {clientUpdateUserPasswordMutation.isLoading && (
          <Spinner classs="me-2" />
        )}
        {translatedFields.updatePassword}
      </button>
    </form>
  );
}
